﻿using GameEntitySystem;
using TemplatesDatabase;

namespace Game
{
    //命令方块电路元素
    public class CommandElectricElement : ElectricElement
    {
        public SubsystemCommand m_subsystemCommand;

        public CommandData m_commandData;

        public float m_voltage;

        public bool clockAllowed = true;

        public SubmitResult m_submitResult;

        public CommandElectricElement(SubsystemElectricity subsystemElectricity, Point3 position)
            : base(subsystemElectricity, new List<CellFace>
            {
                    new CellFace(position.X, position.Y, position.Z, 0),
                    new CellFace(position.X, position.Y, position.Z, 1),
                    new CellFace(position.X, position.Y, position.Z, 2),
                    new CellFace(position.X, position.Y, position.Z, 3),
                    new CellFace(position.X, position.Y, position.Z, 4),
                    new CellFace(position.X, position.Y, position.Z, 5)
            })
        {
            m_commandData = subsystemElectricity.Project.FindSubsystem<SubsystemCommandBlockBehavior>(true).GetCommandData(position);
            m_subsystemCommand = subsystemElectricity.Project.FindSubsystem<SubsystemCommand>(true);
            m_submitResult = SubmitResult.Fail;
        }

        public override float GetOutputVoltage(int face)
        {
            return m_voltage;
        }

        public override bool Simulate()
        {
            try
            {
                if (m_commandData == null) return false;
                if (m_commandData.Mode == WorkingMode.执行)
                {
                    if (base.CalculateHighInputsCount() > 0)
                    {
                        m_submitResult = m_subsystemCommand.Submit(m_commandData.Name, m_commandData, false);
                        m_subsystemCommand.ShowSubmitTips(string.Empty, true, m_submitResult, m_commandData);
                        return m_submitResult == SubmitResult.Success;
                    }
                }
                else if (m_commandData.Mode == WorkingMode.条件)
                {
                    base.SubsystemElectricity.QueueElectricElementForSimulation(this, base.SubsystemElectricity.CircuitStep + 1);
                    if (m_submitResult != SubmitResult.Success && m_submitResult != SubmitResult.Fail)
                    {
                        return false;
                    }
                    m_submitResult = m_subsystemCommand.Submit(m_commandData.Name, m_commandData, true);
                    if (m_submitResult != SubmitResult.OutRange && m_submitResult != SubmitResult.Invalid)
                    {
                        m_subsystemCommand.ShowSubmitTips(string.Empty, true, m_submitResult, m_commandData);
                    }
                    m_voltage = (m_submitResult == SubmitResult.Success) ? 1f : 0f;
                    return true;
                }
                else if (m_commandData.Mode == WorkingMode.变量)
                {
                    m_voltage = 0f;
                    int[] signals = GetSignals();
                    int clockSignal = signals[4];
                    if (IsVariableSyncMode())
                    {
                        if (clockSignal >= 8 && clockAllowed)
                        {
                            clockAllowed = false;
                            return VariableSubmit(signals);
                        }
                        if (clockSignal < 8) clockAllowed = true;
                    }
                    else
                    {
                        if (signals[0] > 0 || signals[1] > 0 || signals[2] > 0 || signals[3] > 0)
                        {
                            return VariableSubmit(signals);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Log.Warning("CommandElectricElement:" + e.Message);
            }
            return false;
        }

        public bool VariableSubmit(int[] signals)
        {
            m_commandData = DataHandle.SetVariableData(m_commandData, signals);
            m_submitResult = m_subsystemCommand.Submit(m_commandData.Name, m_commandData, false);
            m_subsystemCommand.ShowSubmitTips(string.Empty, true, m_submitResult, m_commandData);
            if (m_submitResult == SubmitResult.Success)
            {
                m_voltage = 1f;
                return true;
            }
            else
            {
                m_voltage = 0f;
                return false;
            }
        }

        public int[] GetSignals()
        {
            int[] signals = new int[6];
            foreach (ElectricConnection connection in base.Connections)
            {
                if (connection.ConnectorType != ElectricConnectorType.Output && connection.NeighborConnectorType != 0)
                {
                    switch (connection.NeighborConnectorFace)
                    {
                        case 0: signals[0] = (int)MathF.Round(connection.NeighborElectricElement.GetOutputVoltage(0) * 15f); break;
                        case 1: signals[1] = (int)MathF.Round(connection.NeighborElectricElement.GetOutputVoltage(1) * 15f); break;
                        case 2: signals[2] = (int)MathF.Round(connection.NeighborElectricElement.GetOutputVoltage(2) * 15f); break;
                        case 3: signals[3] = (int)MathF.Round(connection.NeighborElectricElement.GetOutputVoltage(3) * 15f); break;
                        case 4: signals[4] = (int)MathF.Round(connection.NeighborElectricElement.GetOutputVoltage(4) * 15f); break;
                        case 5: signals[5] = (int)MathF.Round(connection.NeighborElectricElement.GetOutputVoltage(5) * 15f); break;
                        default: break;
                    }
                }
            }
            return signals;
        }

        public bool IsVariableSyncMode()
        {
            bool clockConnection = false;
            foreach (ElectricConnection connection in base.Connections)
            {
                if (connection.ConnectorType != ElectricConnectorType.Output && connection.NeighborConnectorType != 0)
                {
                    ElectricConnectorDirection? connectorDirection = SubsystemElectricity.GetConnectorDirection(base.CellFaces[0].Face, 0, connection.ConnectorFace);
                    if (connectorDirection.HasValue)
                    {
                        if (connectorDirection == ElectricConnectorDirection.Bottom) clockConnection = true;
                    }
                }
            }
            return clockConnection;
        }
    }

    //命令方块数据
    public class CommandData(Point3 position, string line)
	{
        public Point3 Position = position;
        public string Line = line;
        public string Name;
        public string Type = "default";
        public WorkingMode Mode = WorkingMode.执行;
        public CoordinateMode Coordinate = CoordinateMode.绝对;
        public Dictionary<string, object> Data = [];
        public Dictionary<string, string> DataText = [];
        public bool OutRange = false;
        public bool Valid = true;
        public Dictionary<string, object> DIYPara = [];

		public object GetValue(string key)
        {
            if (Data.TryGetValue(key, out object value))
            {
                return value;
            }
            return null;
        }

        public string TrySetValue()
        {
            if (Line == "" || string.IsNullOrEmpty(Line))
            {
                Valid = false;
                return string.Empty;
            }
            try
            {
                Line = Line.Replace("\n", " ");
                string[] words = Line.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (!words[0].Contains(":")) words[0] = "cmd:" + words[0];
                string[] firstWord = words[0].Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                Name = firstWord[1];
                Mode = WorkingMode.执行;
                if (firstWord[0].ToLower() == "if")
                {
                    Mode = WorkingMode.条件;
                }
                else
                {
                    if (DataHandle.IsContainsVariable(Line))
                    {
                        Mode = WorkingMode.变量;
                    }
                }
                for (int i = 0; i < words.Length; i++)
                {
                    if (!words[i].Contains(":") && words[i].Contains("@")) words[i] = "cd:" + words[i];
                    string[] keyValue = words[i].Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                    string key = keyValue[0].ToLower();
                    switch (key)
                    {
                        case "type": Type = InstructionManager.GetCommandType(Name, (Mode == WorkingMode.条件), keyValue[1].ToLower()); break;
                        case "cd":
                            {
                                if (keyValue[1].ToLower() == "@c") Coordinate = CoordinateMode.相对;
                                else if (keyValue[1].ToLower() == "@pl") Coordinate = CoordinateMode.玩家;
                                break;
                            }
                        default: break;
                    }
                    if (!InstructionManager.IsFixedParameter(key))
                    {
                        DataText[key] = keyValue[1];
                    }
                }
                foreach (string word in words)
                {
                    string[] keyValue = word.Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                    if (Mode == WorkingMode.变量)
                    {
                        if (DataHandle.IsContainsVariable(keyValue[0] + ":" + keyValue[1]))
                        {
                            Data[keyValue[0]] = keyValue[1];
                            continue;
                        }
                    }
                    SetDataPara(keyValue[0].ToLower(), keyValue[1]);
                }
                if (Mode != WorkingMode.变量)
                {
                    Instruction instruction = InstructionManager.GetInstruction(Name, Mode == WorkingMode.条件);
                    if (instruction != null)
                    {
                        foreach (string key in Data.Keys)
                        {
                            if (instruction.Ranges.TryGetValue(Type + "$" + key, out Point2 range))
                            {
                                int dataValue = (int)Data[key];
                                if (!(dataValue >= range.X && dataValue <= range.Y))
                                {
                                    OutRange = true;
                                    string p = InstructionManager.GetParameterName(Name, Type, key, Mode == WorkingMode.条件);
                                    return string.Format("{0}指令的{1}(类型:{2})的参数范围为{3}-{4}，请纠正数值", Name, p, Type, range.X, range.Y);
                                }
                            }
                        }
                    }
                    else
                    {
                        Valid = false;
                        return "当前编辑的指令是无效的，请重新编辑";
                    }
                }
                Valid = true;
                return string.Empty;
            }
            catch
            {
                Valid = false;
                if (Mode == WorkingMode.条件 && DataHandle.IsContainsVariable(Line))
                {
                    return "条件判断模式不支持指令变量";
                }
                return "当前编辑的指令是无效的，请重新编辑";
            }
        }

        public void FastSetValue()
        {
            foreach (var varKey in DataText.Keys)
            {
                if (!DataHandle.IsContainsVariable(varKey + ":" + DataText[varKey])) continue;
                try
                {
                    SetDataPara(varKey.ToLower(), Data[varKey].ToString());
                }
                catch
                {
                }
            }
        }

        public void SetDataPara(string key, string value)
        {
            switch (key)
            {
                case "cmd": break;
                case "if": break;
                case "type": break;
                case "cd": break;
                case "vec2": Data[key] = DataHandle.GetVector2Value(value); break;
                case "vec3": Data[key] = DataHandle.GetVector3Value(value); break;
                case "vec4": Data[key] = DataHandle.GetVector4Value(value); break;
                default:
                    {
                        if (key.StartsWith("id")) Data[key] = DataHandle.GetNaturalValue(value);
                        else if (key.StartsWith("fid")) Data[key] = DataHandle.GetNaturalValue(value);
                        else if (key.StartsWith("pos")) Data[key] = DataHandle.GetPoint3Value(value);
                        else if (key.StartsWith("eyes")) Data[key] = DataHandle.GetPoint2Value(value);
                        else if (key.StartsWith("con")) Data[key] = DataHandle.GetBoolValue(value);
                        else if (key.StartsWith("color")) Data[key] = DataHandle.GetColorValue(value);
                        else if (key.StartsWith("text")) Data[key] = DataHandle.CharacterEscape(value);
                        else if (key.StartsWith("obj")) Data[key] = value.ToLower();
                        else if (key.StartsWith("time")) Data[key] = DataHandle.GetDateTimeValue(value);
                        else if (key.StartsWith("fix")) Data[key] = DataHandle.CharacterEscape(value);
                        else if (key.StartsWith("opt")) Data[key] = DataHandle.CharacterEscape(value);
                        else if (int.TryParse(value, out int num)) Data[key] = num;
                        else Data[key] = value;
                    }
                    break;
            }
        }
    }

    //命令方块系统
    public class SubsystemCommand : Subsystem
    {
        private static readonly Dictionary<string, Func<CommandData, SubmitResult>> m_functions = new Dictionary<string, Func<CommandData, SubmitResult>>();

        private static readonly Dictionary<string, Func<CommandData, SubmitResult>> m_conditions = new Dictionary<string, Func<CommandData, SubmitResult>>();

        public ComponentPlayer m_componentPlayer;

        public GameMode m_gameMode;

        public bool m_canWorking = true;

        public static bool MessageTips;

        public static bool ChartletDraw;

        public static bool InteractEnable;

        public override void Load(ValuesDictionary valuesDictionary)
        {
            if (!valuesDictionary.ContainsKey("Class")) return;
            string className = valuesDictionary.GetValue<string>("Class");
            if (className != "Game.SubsystemCommand") return;
            MessageTips = valuesDictionary.GetValue<bool>("MessageTips");
            ChartletDraw = valuesDictionary.GetValue<bool>("ChartletDraw");
            InteractEnable = valuesDictionary.GetValue<bool>("InteractEnable");
			AddFunction("moremode", delegate (CommandData commandData)
            {
                //moremode type:default
                if (commandData.Type == "default")
                {
                    //暂无
                }
                //moremode type:notips con:true
                else if (commandData.Type == "notips")
                {
                    bool con = (bool)commandData.GetValue("con");
                    MessageTips = !con;
                }
                //moremode type:nochartlet con:true
                else if (commandData.Type == "nochartlet")
                {
                    bool con = (bool)commandData.GetValue("con");
                    ChartletDraw = !con;
                }
                //moremode type:nointeract con:true
                else if (commandData.Type == "nointeract")
                {
                    bool con = (bool)commandData.GetValue("con");
                    InteractEnable = !con;
                }
                return SubmitResult.Success;
            });
        }

        public override void Save(ValuesDictionary valuesDictionary)
        {
            if (!valuesDictionary.ContainsKey("Class")) return;
            string className = valuesDictionary.GetValue<string>("Class");
            if (className != "Game.SubsystemCommand") return;
            valuesDictionary.SetValue("MessageTips", MessageTips);
            valuesDictionary.SetValue("ChartletDraw", ChartletDraw);
            valuesDictionary.SetValue("InteractEnable", InteractEnable);
        }

        public override void OnEntityAdded(Entity entity)
        {
            ComponentPlayer player = entity.FindComponent<ComponentPlayer>();
            if (player != null)
            {
                m_componentPlayer = player;
                m_gameMode = m_componentPlayer.m_subsystemGameInfo.WorldSettings.GameMode;
            }
        }

        public override void Dispose()
        {
            m_functions.Clear();
            m_conditions.Clear();
        }

        public static void AddFunction(string name, Func<CommandData, SubmitResult> action)
        {
            if (!m_functions.ContainsKey(name))
            {
                m_functions.Add(name, action);
            }
            else
            {
                m_functions[name] += action;
            }
        }

        public static void AddCondition(string name, Func<CommandData, SubmitResult> action)
        {
            if (!m_conditions.TryAdd(name, action))
            {
                m_conditions[name] += action;
            }
        }

        public SubmitResult Submit(string name, CommandData commandData, bool Judge)
        {
            if (!m_canWorking) return SubmitResult.Fail;
            Dictionary<string, Func<CommandData, SubmitResult>> commands = (!Judge) ? m_functions : m_conditions;
            if (commands.TryGetValue(name, out Func<CommandData, SubmitResult> action))
            {
                if (!commandData.Valid) return SubmitResult.Invalid;
                if (commandData.OutRange) return SubmitResult.OutRange;
                try
                {
                    return action.Invoke(commandData);
                }
                catch (Exception e)
                {
                    Log.Warning("CommandManager:" + e.Message);
                    return SubmitResult.Exception;
                }
            }
            return SubmitResult.NoFound;
        }

        public void ShowSubmitTips(string tip, bool onlyError = false, SubmitResult result = SubmitResult.Success, CommandData commandData = null)
        {
            if (!MessageTips) return;
            if (onlyError)
            {
                string errorposition = string.Format(";\n发生错误的命令方块位置:({0},{1},{2})", commandData.Position.X, commandData.Position.Y, commandData.Position.Z);
                switch (result)
                {
                    case SubmitResult.Exception: m_componentPlayer?.ComponentGui.DisplaySmallMessage("错误:提交的" + commandData.Name + "指令发生异常，请重新编辑" + errorposition, Color.Yellow, false, false); break;
                    case SubmitResult.NoFound: m_componentPlayer?.ComponentGui.DisplaySmallMessage("错误:提交的" + commandData.Name + "指令不存在，请核对指令名称" + errorposition, Color.Yellow, false, false); break;
                    case SubmitResult.Invalid: m_componentPlayer?.ComponentGui.DisplaySmallMessage("错误:提交的" + commandData.Name + "指令存在问题，请重新编辑" + errorposition, Color.Yellow, false, false); break;
                    case SubmitResult.OutRange: m_componentPlayer?.ComponentGui.DisplaySmallMessage("错误:提交的" + commandData.Name + "指令存在参数超出范围，请纠正数值" + errorposition, Color.Yellow, false, false); break;
                    case SubmitResult.Limit: m_componentPlayer?.ComponentGui.DisplaySmallMessage("错误:挑战或残酷模式不支持" + commandData.Name + "指令" + errorposition, Color.Yellow, false, false); break;
                    default: break;
                }
            }
            else
            {
                if (result == SubmitResult.Success)
                {
                    m_componentPlayer?.ComponentGui.DisplaySmallMessage("提示:" + tip, Color.Yellow, false, false);
                }
            }
        }

        public void ShowEditedTips(string tip)
        {
            if (string.IsNullOrEmpty(tip)) return;
            m_componentPlayer?.ComponentGui.DisplaySmallMessage("提示：" + tip, Color.Yellow, false, false);
        }
    }
}